﻿/************************ User Class ******************************/
var User = function (index, name, roomindex) {
  this.index = index;
  this.name = name;
  this.roomindex = roomindex;
  this.status = 'idle';
  this.isOwner = false;
  this.score = 0;
  this.isDrawer = false;
  this.color = '#000000';
  this.lineWidth = 10;
  this.lineCap = 'butt';
  this.toJson = function () {
    return { name: this.name, status: this.status, isDrawer: this.isDrawer, isOwner: this.isOwner, score: this.score };
  };

  this.updatePalette = function (palette) {
    this.color = palette.color;
    this.lineWidth = palette.lineWidth;
    this.lineCap = palette.lineCap;
  };

  this.reset = function () {
    this.isDrawer = false;
    this.color = '#000000';
    this.lineWidth = 10;
    this.lineCap = 'butt';
  }
};

/************************ Room Class ******************************/
var Room = function (index, owner, socket) {
  this.init(index, owner, socket);
}
Room.prototype = {
  // check if all users get ready
  isReady: function () {
    for (var i = 0; i < this.sockets.length; i++) {
      if (this.sockets[i].user.status != 'ready') return false;
    }
    return true;
  },

  addUser: function (name, socket) {
    var newUser = new User(this.count, name, this.index);
    // bind user information to socket instance
    socket.user = newUser;
    this.sockets.push(socket);
    this.count = this.count + 1;
    return newUser;
  },

  removeUser: function (socket) {
    for (var i = 0; i < this.sockets.length; i++) {
      if (this.sockets[i].user.name == socket.user.name) {
        this.sockets.splice(i, 1);
      }
    }
    this.count = this.count - 1;
  },

  findUser: function (name) {
    for (var i = 0; i < this.sockets.length; i++) {
      if (this.sockets[i].user.name == name) {
        return this.sockets[i];
      }
    }
    return null;
  },

  updateStatus: function (status, socket) {

    if (status == 'ready') {
      socket.user.status = status;
      // someone gets ready, update room to ready if possible
      this.status = 'ready';
      for (var i = 0; i < this.sockets.length; i++) {
        if (this.sockets[i].user.status != 'ready') {
          this.status = 'waiting'; return;
        }
      }
    } else if (status == 'cancel') {
      socket.user.status = 'idle';
      // someone cancel ready, update room to waiting
      this.status = 'waiting';
    } else if (status == 'startgame') {
      // update all user status to be playing
      //      for (var i = 0; i < this.sockets.length; i++) {
      //        this.sockets[i].user.status = 'playing';
      //      }
      NewGame(this.index);
    } else if (status == 'giveupgame') {
      // someone give up the game
      GiveUpGame(this, socket);
    }

  },

  getUsers: function () {
    var users = [];
    for (var i = 0; i < this.sockets.length; i++) {
      users.push(this.sockets[i].user.toJson());
    }
    return users;
  },

  toJson: function () {
    return { index: this.index, name: this.name, status: this.status, drawername: (this.drawer != null) ? this.drawer.user.name : '',
      users: this.getUsers()
    };
  },

  BackToReady: function () {
    if (this.isReady()) {
      this.status = 'ready';
    }
    else {
      this.status = 'waiting';
    }

    this.drawer = null;
    this.drawWord = null;
    for (var i = 0; i < this.sockets.length; i++) {
      this.sockets[i].user.reset();
    }
    this.RoomBroadcast('roominfo', this.toJson());
  },

  init: function (index, owner, socket) {
    this.sockets = []; // an array of user client sockets
    this.count = 0;
    this.reply = 0;
    this.name = owner + "'s room";
    this.index = index;
    this.addUser(owner, socket);
    this.sockets[0].user.isOwner = true;
    this.sockets[0].user.status = 'ready';
    this.owner = this.sockets[0];
    this.status = 'waiting';
    this.drawer = null;
    this.drawWord = null;
  },

  RoomBroadcast: function (name, data) {
    for (var i = 0; i < this.sockets.length; i++) {
      this.sockets[i].emit(name, data);
    }
  }
};

/************************ Online Rooms Static class ******************************/
var OnlineRooms = {
  rooms: [],
  createRoom: function (index, owner, socket) {
    var newRoom = new Room(index, owner, socket);
    console.log('Room created. Name:' + newRoom.name + ' index: ' + newRoom.index);
    this.rooms.push(newRoom);
    return newRoom;
  },

  getRoom: function (index) {
    for (var i = 0; i < this.rooms.length; i++) {
      if (this.rooms[i].index == index) {
        return this.rooms[i];
      }
    }

    return null;
  },

  removeRoom: function (index) {
    for (var i = 0; i < this.rooms.length; i++) {
      if (this.rooms[i].index == index) {
        this.rooms.splice(i, 1);
      }
    }    
  },

  getRoomList: function () {
    var list = [];
    for (var i = 0; i < this.rooms.length; i++) {
      list.push({
        reply: this.rooms[i].reply,
        name: this.rooms[i].name,
        status: this.rooms[i].status,
        count: this.rooms[i].count,
        index: this.rooms[i].index
      });
    }

    return list;
  }
};

//OnlineRooms.createRoom(0, 'game', { emit: function (name, data) { } });

/************************ The web scoket application ******************************/
var app = require('express').createServer(), io = require('socket.io').listen(app);
var usercount = 0;
var words = ['milk', 'cow', 'sheep', 'boy', 'table', 'glasses', 'car', 'tank', 'cat', 'flower', 'school', 'book', 'apple', 'orange', 'house', 'ruler'];
var drawWord;
var port = process.env.PORT || 4579;
app.listen(port, function () {
  console.log("Listening on " + port);
});

io.sockets.on('connection', function (socket) {
  socket.user = null;
  socket.on('roomlist', function (msg) {
    socket.emit('roomlist', OnlineRooms.getRoomList());
  });

  socket.on('user message', function (roomindex, msg) {
    var room = OnlineRooms.getRoom(roomindex);
    RoomBroadcast('user message', room, { name: socket.user.name, msg: msg });

    if (room.status == 'playing' && room.drawWord != null && msg.indexOf(room.drawWord) != -1 && !socket.user.isDrawer) {
      //someone guess it
      RoomBroadcast('notice', room, socket.user.name + ' win!');
      room.BackToReady();
    }
  });

  socket.on('clear', function (roomindex) {
    RoomBroadcast('clear', OnlineRooms.getRoom(roomindex), '');
  });

  socket.on('palette', function (roomindex, palette) {
    socket.user.updatePalette(palette);
    RoomBroadcast('roominfo', OnlineRooms.getRoom(roomindex), OnlineRooms.getRoom(roomindex).toJson());
  });

  socket.on('mousemove', function (roomindex, msg) {
    console.log('[' + msg.offsetX + ', ' + msg.offsetY + ']');
    RoomBroadcast('mousemove', OnlineRooms.getRoom(roomindex), { name: socket.user.name, offsetX: msg.offsetX, offsetY: msg.offsetY, color: socket.user.color });
  });

  socket.on('mousedown', function (roomindex, msg) {
    console.log('[' + msg.offsetX + ', ' + msg.offsetY + ']');
    RoomBroadcast('mousedown', OnlineRooms.getRoom(roomindex), { name: socket.user.name, offsetX: msg.offsetX, offsetY: msg.offsetY });
  });

  socket.on('mouseup', function (roomindex, msg) {
    console.log('[' + msg.offsetX + ', ' + msg.offsetY + ']');
    RoomBroadcast('mouseup', OnlineRooms.getRoom(roomindex), { name: socket.user.name, offsetX: msg.offsetX, offsetY: msg.offsetY });
  });

  socket.on('nickname', function (roomindex, nick, fn) {
    var room = OnlineRooms.getRoom(roomindex);
    if (room == null) {
      // create a room if not exist
      room = OnlineRooms.createRoom(roomindex, nick, socket);
      console.log('=================================' + 'Room ' + room.index + ' is created');
      fn(false);
    } else {
      console.log('=================================Room ' + room.index + ' is entered');
      if (room.findUser(nick) != null) {
        // user name already exists
        fn(true);
        return;
      } else {
        socket.user = room.addUser(nick, socket)
      }
    }
    socket.broadcast.emit('roomlist', OnlineRooms.getRoomList());
    socket.broadcast.emit('announcement', room.index, socket.user.name + ' connected');
    RoomBroadcast('roominfo', room, room.toJson());
  });

  socket.on('startgame', function (roomindex) {
    OnlineRooms.getRoom(roomindex).updateStatus('startgame', socket);
  });

  socket.on('ready', function (roomindex) {
    var room = OnlineRooms.getRoom(roomindex);
    room.updateStatus('ready', socket);
    RoomBroadcast('roominfo', room, room.toJson());
  });

  socket.on('cancel', function (roomindex) {
    var room = OnlineRooms.getRoom(roomindex);
    room.updateStatus('cancel', socket);
    RoomBroadcast('roominfo', room, room.toJson());
  });

  socket.on('giveupgame', function (roomindex) {
    OnlineRooms.getRoom(roomindex).updateStatus('giveupgame', socket);
  });

  socket.on('disconnect', function () {
    if (socket.user == null) return;
    console.log('=================================' + socket.user.name + ' disconnected. Room Index: ' + socket.user.roomindex);
    var room = OnlineRooms.getRoom(socket.user.roomindex);
    room.removeUser(socket);
    if (room.count == 0) {
      // remove this room
      console.log('=================================' + 'Room ' + room.index + ' is removed');
      OnlineRooms.removeRoom(room.index);
      socket.broadcast.emit('roomlist', OnlineRooms.getRoomList());
    } else {
      if (socket.user.isOwner) {
        // select a radom room owner
        RoomOwnerMigration(room);
      }

      if (room.status == 'playing') {
        if (socket.user.isDrawer) {
          //automatically give up
          GiveUpGame(room, socket);
        } else {
          // do nothing
        }
      }

      RoomBroadcast('roominfo', room, room.toJson());
    }
    socket.broadcast.emit('announcement', socket.user.name + ' disconnected');
  });
});

function NewGame(roomindex) {
  var room = OnlineRooms.getRoom(roomindex);
  console.log('=============================NewGame roomindex: ' + roomindex + ' count: ' + room.count);
  if (room.count < 2) {
    room.owner.emit('notice', 'User count less than 2, please wait for more player to join!');
    return;
  }

  if (!room.isReady()) {
    room.owner.emit('notice', 'User are not all ready.');
    return;
  }

  room.status = 'playing';
  var radomUserIndex = parseInt(Math.random() * room.count);
  var drawerSocket = room.sockets[radomUserIndex];
  var radomWordIndex = parseInt(Math.random() * words.length);
  drawWord = words[radomWordIndex];
  room.drawWord = drawWord;
  RoomBroadcast('announcement', room, drawerSocket.user.name + ' is the new drawer');
  RoomBroadcast('clear', OnlineRooms.getRoom(roomindex), '');
  drawerSocket.emit('drawWord', drawWord);
  drawerSocket.user.isDrawer = true;
  room.drawer = drawerSocket;
  RoomBroadcast('roominfo', room, room.toJson());
}

function GiveUpGame(room, socket) {
  console.log('==============================' + socket.user.name + ' give up the game. Game restart');
  socket.user.isDrawer = false;
  socket.user.score -= 20;
  RoomBroadcast('announcement', room, socket.user.name + ' gave up the game');
  room.status = 'waiting';
  NewGame(room.index);
}

function RoomBroadcast(name, room, data) {
  for (var i = 0; i < room.sockets.length; i++) {
    room.sockets[i].emit(name, data);
  }
}

function RoomOwnerMigration(room) {
  var radomUserIndex = parseInt(Math.random() * room.count);
  var owner = room.sockets[radomUserIndex];
  owner.user.isOwner = true;
  owner.user.status = 'ready';
  room.owner = owner;
  RoomBroadcast('roominfo', room, room.toJson());
}
